#ifndef SIMPLE_SUPPORT_META_FIND_HPP
#define SIMPLE_SUPPORT_META_FIND_HPP

#include <type_traits> // std::enable_if_t std::conditional_t std::is_same
#include "bind.hpp" // bind
#include "list.hpp" // list_size_v list_element_t

namespace simple::support::meta
{

	template <size_t I, typename T, typename Functor>
	struct find_result
	{
		static constexpr size_t value = I;
		using type = T;
		using functor = Functor;
	};
	template <size_t I, typename Functor>
	struct find_result_end
	{
		static constexpr size_t value = I;
		using functor = Functor;
	};

	template<typename Functor, typename List, size_t begin = 0,
		typename Enable = void>
	struct find {};
	template<typename Functor, typename List, size_t begin>
	struct find<Functor, List, begin,
		std::enable_if_t<(begin < list_size_v<List>)>>
	{
		private:
		using element_t = list_element_t<begin, List>;
		using result = typename Functor::template bound<element_t>;

		using rebound = rebind_t<Functor, result>;

		public:
		using type = std::conditional_t< result::value,
			find_result<begin, element_t, rebound>,
			typename find<rebound, List, begin+1>::type >;

	};
	template<typename Functor, typename List, size_t begin>
	struct find<Functor, List, begin,
		std::enable_if_t<(begin == list_size_v<List>)>>
	{
		using type = find_result_end<list_size_v<List>, Functor>;
	};
	template<typename Functor, typename List, size_t begin = 0>
	using find_t = typename find<Functor, List, begin>::type;

	template<typename T, typename List, size_t begin = 0>
	constexpr auto find_v = find_t<
		bind<std::is_same, T>,
		List, begin
	>::value;

	template<template <typename...> typename Op, typename List, size_t begin = 0>
	using find_if_t = typename find_t<
		bind<Op>, List, begin
	>::type;

	template<template <typename...> typename Op, typename List, size_t begin = 0>
	constexpr auto find_if_v = find_t<
		bind<Op>, List, begin
	>::value;

} // namespace simple::support::meta

#endif /* end of include guard */
